package cn.alex.sso.framework.util;

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * 构建树级工具类
 *
 * @author Alex
 * @date 2024/10/5 16:18
 */
public class TreeBuilder {
    /**
     * 构造树型结构数据
     *
     * @param data     数据源
     * @param id       id字段，默认 'id'
     * @param parentId 父节点字段，默认 'parentId'
     * @param children 孩子节点字段，默认 'children'
     * @param <T>      泛型类型
     * @return 树形结构
     */
    public static <T> List<T> build(List<T> data,
                                    String id,
                                    String parentId,
                                    String children) {
        String configId = id != null ? id : "id";
        String configParentId = parentId != null ? parentId : "parentId";
        String configChildren = children != null ? children : "children";

        Map<String, List<T>> childrenListMap = new HashMap<>();
        Map<String, T> nodeIds = new HashMap<>();
        List<T> tree = new ArrayList<>();

        // 构建父子关系
        for (T d : data) {
            String parentIdValue = convertToString(getField(d, configParentId));
            childrenListMap.computeIfAbsent(parentIdValue, k -> new ArrayList<>());
            nodeIds.put(convertToString(getField(d, configId)), d);
            childrenListMap.get(parentIdValue).add(d);
        }

        // 寻找根节点
        for (T d : data) {
            String parentIdValue = convertToString(getField(d, configParentId));
            if (nodeIds.get(parentIdValue) == null) {
                tree.add(d);
            }
        }

        // 递归构建子节点
        for (T t : tree) {
            adaptToChildrenList(t, configId, configChildren, childrenListMap);
        }

        return tree;
    }

    private static <T> void adaptToChildrenList(T node,
                                                String configId,
                                                String configChildren,
                                                Map<String, List<T>> childrenListMap) {
        String nodeId = convertToString(getField(node, configId));
        if (childrenListMap.get(nodeId) != null) {
            setField(node, configChildren, childrenListMap.get(nodeId));
        }
        if (getField(node, configChildren) != null) {
            @SuppressWarnings("unchecked")
            List<T> children = (List<T>) getField(node, configChildren);
            for (T child : children) {
                adaptToChildrenList(child, configId, configChildren, childrenListMap);
            }
        }
    }

    private static <T> Object getField(T object, String fieldName) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            return field.get(object);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException("Error getting field: " + fieldName, e);
        }
    }

    private static <T> void setField(T object, String fieldName, Object value) {
        try {
            Field field = object.getClass().getDeclaredField(fieldName);
            field.setAccessible(true);
            field.set(object, value);
        } catch (NoSuchFieldException | IllegalAccessException e) {
            throw new RuntimeException("Error setting field: " + fieldName, e);
        }
    }

    public static String convertToString(Object value) {
        if (value instanceof Long) {
            return value.toString();
        } else if (value instanceof String) {
            return (String) value;
        } else {
            throw new IllegalArgumentException("Unsupported type: " + value.getClass().getName());
        }
    }
}
